#include "stdafx.h"
#include "encrypt.h"

#include <WinCrypt.h>

namespace shared
{
	namespace encrypt {
		typedef struct
		{
			BLOBHEADER header;
			DWORD cbKeySize;
			BYTE rgbKeyData[16];
		}AESKeyBlob;

		char* AES_KEY = "sumscopeDataSync";
		//0392039203920300
		//const BYTE AES_IV[] = { 0x30, 0x33, 0x39, 0x32, 0x30, 0x33, 0x39, 0x32, 0x30, 0x33, 0x39, 0x32, 0x30, 0x33, 0x30, 0x30 };
		TCHAR* wszProvider[] = { MS_ENH_RSA_AES_PROV, MS_ENH_RSA_AES_PROV_XP };

		int AesDecrypt(const char* input, int inlen, char* output, int& outlen)
		{
			HCRYPTPROV hProv = NULL;
			HCRYPTKEY hSessionKey = NULL;
			char* out_buf = NULL;
			BOOL bResult = TRUE;
			int ret = -1;
			DWORD need_len = 0;;

			AESKeyBlob blob;
			blob.header.bType = PLAINTEXTKEYBLOB;
			blob.header.bVersion = CUR_BLOB_VERSION;
			blob.header.reserved = 0;
			blob.header.aiKeyAlg = CALG_AES_128;
			blob.cbKeySize = 16;
			memcpy(blob.rgbKeyData, AES_KEY, 16);

			for (size_t i = 0; i < sizeof(wszProvider) / sizeof(wszProvider[0]); i++)
			{
				bResult = CryptAcquireContext(&hProv, NULL, wszProvider[i], PROV_RSA_AES, 0);
				if (bResult != 0 && hProv)
				{
					break;
				}
			}

			if (bResult == 0 || hProv == NULL)
			{
				goto faile;
			}

			bResult &= CryptImportKey(hProv, (BYTE*)&blob, sizeof(blob), 0, 0, &hSessionKey);
			if (bResult == 0 || hSessionKey == NULL)
			{
				goto faile;
			}
			/*bResult &= CryptSetKeyParam(hSessionKey, KP_IV, (BYTE*)AES_IV, 0);
			if (bResult == 0)
			{
				goto faile;
			}*/

			DWORD dwMode = /*CRYPT_MODE_CBC*/CRYPT_MODE_ECB;
			bResult &= CryptSetKeyParam(hSessionKey, KP_MODE, (BYTE*)&dwMode, 0);
			if (bResult == FALSE)
			{
				goto faile;
			}

			/*DWORD data_len = in_len;
			if (data_len>MAX_INPUT_LEN)
			{
				ret = -9;
				goto faile;
			}*/
			out_buf = new char[inlen * 4];
			memcpy(out_buf, input, inlen);
			need_len = inlen;
			bResult &= CryptDecrypt(hSessionKey, NULL, TRUE, 0, (BYTE*)out_buf, &need_len);
			if (bResult == 0)
			{
				printf("Error %x during CryptEncrypt()!\n", GetLastError());
				ret = -2;
				goto faile;
			}

			if (need_len > outlen)
			{
				ret = -3;
				goto faile;
			}

			memcpy(output, out_buf, need_len);
			outlen = need_len;

			bResult &= CryptDestroyKey(hSessionKey);
			bResult &= CryptReleaseContext(hProv, 0);
			delete[] out_buf;
			out_buf = NULL;

			return 0;
		faile:
			if (hSessionKey)
			{
				CryptDestroyKey(hSessionKey);
				hSessionKey = NULL;
			}
			if (hProv)
			{
				CryptReleaseContext(hProv, 0);
				hProv = NULL;
			}
			if (out_buf)
			{
				delete[] out_buf;
				out_buf = NULL;
			}
			return ret;
		}

		int AesEncrypt(const char* input, int inlen, char* output, int& outlen)
		{
			HCRYPTPROV hProv = NULL;
			HCRYPTKEY hSessionKey = NULL;
			char* out_buf = NULL;
			BOOL bResult = TRUE;
			int ret = -1;
			DWORD need_len = 0;;

			AESKeyBlob blob;
			blob.header.bType = PLAINTEXTKEYBLOB;
			blob.header.bVersion = CUR_BLOB_VERSION;
			blob.header.reserved = 0;
			blob.header.aiKeyAlg = CALG_AES_128;
			blob.cbKeySize = 16;
			memcpy(blob.rgbKeyData, AES_KEY, 16);

			for (size_t i = 0; i < sizeof(wszProvider) / sizeof(wszProvider[0]); i++)
			{
				bResult = CryptAcquireContext(&hProv, NULL, wszProvider[i], PROV_RSA_AES, 0);
				if (bResult != 0 && hProv)
				{
					break;
				}
			}

			if (bResult == 0 || hProv == NULL)
			{
				goto faile;
			}

			bResult &= CryptImportKey(hProv, (BYTE*)&blob, sizeof(blob), 0, 0, &hSessionKey);
			if (bResult == 0 || hSessionKey == NULL)
			{
				goto faile;
			}

			/*bResult &= CryptSetKeyParam(hSessionKey, KP_IV, (BYTE*)AES_IV, 0);
			if (bResult == 0)
			{
				goto faile;
			}*/

			DWORD dwMode = /*CRYPT_MODE_CBC*/CRYPT_MODE_ECB;
			bResult &= CryptSetKeyParam(hSessionKey, KP_MODE, (BYTE*)&dwMode, 0);
			if (bResult == FALSE)
			{
				goto faile;
			}

			dwMode = PKCS5_PADDING;
			bResult &= CryptSetKeyParam(hSessionKey, KP_PADDING, (BYTE*)&dwMode, 0);
			if (bResult == FALSE)
			{
				goto faile;
			}

			/* dwMode = 128;
			 bResult &= CryptSetKeyParam(hSessionKey, KP_MODE_BITS, (BYTE*)&dwMode, 0);
			 if (bResult == FALSE)
			 {
				 goto faile;
			 }*/

			out_buf = new char[inlen * 4];
			memcpy(out_buf, input, inlen);
			need_len = inlen;
			bResult &= CryptEncrypt(hSessionKey, NULL, TRUE, 0, (BYTE*)out_buf, &need_len, inlen * 4);
			if (bResult == 0)
			{
				printf("Error %x during CryptEncrypt()!\n", GetLastError());
				ret = -2;
				goto faile;
			}

			if (need_len > outlen)
			{
				ret = -3;
				goto faile;
			}

			memcpy(output, out_buf, need_len);
			outlen = need_len;

			bResult &= CryptDestroyKey(hSessionKey);
			bResult &= CryptReleaseContext(hProv, 0);
			delete[] out_buf;
			out_buf = NULL;

			return 0;
		faile:
			if (hSessionKey)
			{
				CryptDestroyKey(hSessionKey);
				hSessionKey = NULL;
			}
			if (hProv)
			{
				CryptReleaseContext(hProv, 0);
				hProv = NULL;
			}
			if (out_buf)
			{
				delete[] out_buf;
				out_buf = NULL;
			}
			return ret;
		}

		const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		std::string  Base64Encode(const char* input, int len)
		{
			unsigned char const* orig = (unsigned char const*)input; // in case any input bytes have the MSB set
			if (orig == NULL) return NULL;

			unsigned const numOrig24BitValues = len / 3;
			bool havePadding = len > numOrig24BitValues * 3;
			bool havePadding2 = len == numOrig24BitValues * 3 + 2;
			unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);
			char* result = new char[numResultBytes + 3]; // allow for trailing '/0'

														 // Map each full group of 3 input bytes into 4 output base-64 characters:
			unsigned i;
			for (i = 0; i < numOrig24BitValues; ++i)
			{
				result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
				result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
				result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F];
				result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F];
			}

			// Now, take padding into account.  (Note: i == numOrig24BitValues)
			if (havePadding)
			{
				result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
				if (havePadding2)
				{
					result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
					result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F];
				}
				else
				{
					result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F];
					result[4 * i + 2] = '=';
				}
				result[4 * i + 3] = '=';
			}

			result[numResultBytes] = '\0';
			std::string s = result;
			delete[]result;
			return s;
		}
	}
}